# account.py
from datetime import datetime, timezone
from typing import List, Dict, Any, Tuple, Optional


class Account:
    """
    管理回测中的财务状况。
    """

    def __init__(self, initial_balance: float = 10000.0, currency: str = "USD"):
        self.initial_balance: float = initial_balance  # 初始资金
        self.current_balance: float = initial_balance  # 当前可用资金 (现金)
        self.currency: str = currency  # 账户货币

        # (时间戳, 权益) 列表，记录权益变化
        self.equity_curve: List[Tuple[datetime, float]] = [(datetime.min, initial_balance)]
        self.trades_history: List[Dict[str, Any]] = []  # 存储已平仓的交易详情

        # 性能指标
        self.total_trades: int = 0  # 总交易次数
        self.winning_trades: int = 0  # 盈利交易次数
        self.losing_trades: int = 0  # 亏损交易次数
        self.total_profit: float = 0.0  # 总盈利 (已实现)
        self.total_loss: float = 0.0  # 总亏损 (已实现, 存为正值)
        self.max_drawdown: float = 0.0  # 最大回撤比例
        self._peak_equity: float = initial_balance  # 记录过的最高权益
        self.total_fees_paid: float = 0.0  # 支付的总手续费

        # 用于计算持仓的浮动盈亏
        # 如果逻辑复杂，也可以由投资组合管理器类管理
        self.open_positions_pnl: float = 0.0  # 当前所有持仓的未实现盈亏总和

    @property
    def equity(self) -> float:
        """当前权益，包括未平仓头寸的未实现盈亏。"""
        return self.current_balance + self.open_positions_pnl

    def update_balance(self, amount: float):
        """直接更新余额，例如用于存款/取款 (在回测中不常用)。"""
        self.current_balance += amount

    def record_trade(self, position_data: Dict[str, Any], timestamp: datetime):
        """
        记录一笔已平仓的交易，更新余额和性能指标。
        `position_data` 应为已平仓Position对象的字典。
        """
        self.trades_history.append(position_data)

        pnl = position_data.get("realized_pnl", 0.0)  # 已实现盈亏
        fees = position_data.get("total_fees", 0.0)  # 总手续费

        # 盈亏已经包含了手续费 (如果在Position类中计算正确)
        self.current_balance += pnl
        self.total_fees_paid += fees
        self.total_trades += 1

        if pnl > 0:
            self.winning_trades += 1
            self.total_profit += pnl
        elif pnl < 0:
            self.losing_trades += 1
            self.total_loss += abs(pnl)

        # 更新权益曲线和回撤
        current_equity = self.equity  # 使用包含未实现盈亏的权益
        self.equity_curve.append((timestamp, current_equity))

        if current_equity > self._peak_equity:
            self._peak_equity = current_equity

        drawdown = (self._peak_equity - current_equity) / self._peak_equity if self._peak_equity > 0 else 0
        if drawdown > self.max_drawdown:
            self.max_drawdown = drawdown

    def update_open_positions_pnl(self, total_unrealized_pnl: float, timestamp: datetime):
        """
        由回测引擎在每一步调用，以更新所有当前未平仓头寸的总未实现盈亏。
        这用于权益计算和最大回撤。
        """
        self.open_positions_pnl = total_unrealized_pnl

        # 根据当前市值更新权益曲线和回撤
        current_equity = self.equity
        # 只有当时间前进或这是此时间戳的第一次更新时才添加到权益曲线
        if not self.equity_curve or self.equity_curve[-1][0] < timestamp:
            self.equity_curve.append((timestamp, current_equity))
        elif self.equity_curve[-1][0] == timestamp:  # 如果是相同时间戳，则更新最后一个条目
            self.equity_curve[-1] = (timestamp, current_equity)

        if current_equity > self._peak_equity:
            self._peak_equity = current_equity

        drawdown = (self._peak_equity - current_equity) / self._peak_equity if self._peak_equity > 0 else 0
        if drawdown > self.max_drawdown:
            self.max_drawdown = drawdown

    def get_performance_summary(self) -> Dict[str, Any]:
        """计算并返回性能摘要。"""
        profit_factor = self.total_profit / self.total_loss if self.total_loss > 0 else float('inf')
        win_rate = self.winning_trades / self.total_trades if self.total_trades > 0 else 0.0
        avg_win = self.total_profit / self.winning_trades if self.winning_trades > 0 else 0.0
        avg_loss = self.total_loss / self.losing_trades if self.losing_trades > 0 else 0.0  # total_loss是正值

        return {
            "initial_balance": self.initial_balance,
            "final_balance": self.current_balance,
            "final_equity": self.equity,
            "total_net_profit": self.current_balance - self.initial_balance,
            "profit_pcent": ((
                                         self.current_balance - self.initial_balance) / self.initial_balance) * 100 if self.initial_balance > 0 else 0,
            "total_trades": self.total_trades,
            "winning_trades": self.winning_trades,
            "losing_trades": self.losing_trades,
            "win_rate_pcent": win_rate * 100,
            "average_win": avg_win,
            "average_loss": avg_loss,  # 平均亏损值
            "profit_factor": profit_factor,  # 盈亏比 (总盈利/总亏损)
            "max_drawdown_pcent": self.max_drawdown * 100,
            "total_fees_paid": self.total_fees_paid,
            "equity_curve": self.equity_curve,
            "trades_history": self.trades_history
        }

    def reset(self, initial_balance: Optional[float] = None):
        """将账户重置为初始状态以进行新的回测。"""
        self.initial_balance = initial_balance if initial_balance is not None else self.initial_balance
        self.current_balance = self.initial_balance
        _initial_timestamp = datetime(1, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
        self.equity_curve: List[Tuple[datetime, float]] = [(_initial_timestamp, initial_balance)]
        self.trades_history = []
        self.total_trades = 0
        self.winning_trades = 0
        self.losing_trades = 0
        self.total_profit = 0.0
        self.total_loss = 0.0
        self.max_drawdown = 0.0
        self._peak_equity = self.initial_balance
        self.total_fees_paid = 0.0
        self.open_positions_pnl = 0.0
        print(f"账户已重置，初始资金: {self.initial_balance} {self.currency}")